package com.lions.client.view.basepanel;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;

import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.core.java.util.Arrays;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Image;
import com.lions.client.model.Condition;
import com.lions.client.model.Model;
import com.lions.client.model.Module;
import com.lions.client.model.ModuleList;
import com.lions.client.view.mappanel.MapPanel;
import com.lions.client.view.popup.MyPopupPanel;

public class MinBaseBuilder {
	public MinBaseBuilder(Model m, MapPanel mapPanel, int plain1X, int plain1Y,
			int plain2X, int plain2Y, int plain3X, int plain3Y, int airlockX,
			int airlockY, int controlX, int controlY, int powerX, int powerY,
			int foodX, int foodY, int dormX, int dormY, int canteenX,
			int canteenY, int sanX, int sanY) {

		rows = 50;
		columns = 100;
		model = m;
		externallySet = false;
		centerX = 0;
		centerY = 0;
		this.mapPanel = mapPanel;
		this.map = mapPanel.getMap();
		this.plain1X = plain1X;
		this.plain1Y = plain1Y;
		this.plain2X = plain2X;
		this.plain2Y = plain2Y;
		this.plain3X = plain3X;
		this.plain3Y = plain3Y;
		this.airlockX = airlockX;
		this.airlockY = airlockY;
		this.controlX = controlX;
		this.controlY = controlY;
		this.powerX = powerX;
		this.powerY = powerY;
		this.foodX = foodX;
		this.foodY = foodY;
		this.dormX = dormX;
		this.dormY = dormY;
		this.canteenX = canteenX;
		this.canteenY = canteenY;
		this.sanX = sanX;
		this.sanY = sanY;
		
		// define a 2d container to populate with base modules
		baseContainer = new Module[rows][columns];
		routingList = new ArrayList<Module>();
		notInBase = new Module[rows][columns];
		build();
		movementPoints = calculateMovementPoints();
		//set the order of the modules to be routed (nearest to farthest)
		setRoutingListOrder();
		
	}

	
	public void build() {
		// clear the map for building
		map.clear();
		//make sure the base container is empty
		for (int row = (rows - 1); row >= 0; row--) {
			for (int col = (columns - 1); col >= 0; col--) {
				baseContainer[row][col] = null;
			}
		} 
		// define a 2d container to represent the current list
		Module[][] contain = new Module[rows][columns];


		// get the moduleList
		ModuleList moduleList = model.getModuleList();

		// put the modules in the container
		for (Module module : moduleList) {
			
			if (module.getCondition() == Condition.USABLE){
				contain[module.getY()][module.getX()] = module;
			}
			else{
				notInBase[module.getY()][module.getX()] = module;
			}
		}

		// A list to hold the plain Modules
		LinkedList<Module> plainList = new LinkedList<Module>();

		// define variables for finding the center of the base
		if(!externallySet){
			centerX = this.findCenterX(contain);
			centerY = this.findCenterY(contain);
		}
		
			
		//make sure the base wont go into unusable area
		if(centerX > 40 && centerX < 50 && centerY > 36){
			centerY = 36;
		}
		
		// find the center module: plain module
		for (Module module : moduleList) {
			if (module.getType().toString().equals("PLAIN")) {
				plainList.add(module);
			}
		}
		// these two checks can probably be removed safely
		for (Module module : plainList) {
			if (module.getType().toString() != "PLAIN") {
				System.out
						.println("Something went wrong making the connector list");
			}
		}
		if (plainList.size() < 3) {
			Window.alert("Not enough Connectors");
		}

		// put the connectors into the 2d base container
		if (!plainList.isEmpty()) {
			baseContainer[centerY + plain1Y][centerX + plain1X] = plainList
					.getFirst();
			plainList.remove();
		}
		if (!plainList.isEmpty()) {
			baseContainer[centerY + plain2Y][centerX + plain2X] = plainList
					.getFirst();
			plainList.remove();
		}
		if (!plainList.isEmpty()) {
			baseContainer[centerY + plain3Y][centerX + plain3X] = plainList
					.getFirst();
			plainList.remove();
		}

		// find a canteen
		for (Module module : moduleList) {
			if (module.getType().toString() == "CANTEEN") {
				baseContainer[centerY + canteenY][centerX + canteenX] = module;
			}
		}

		// find an airlock
		for (Module module : moduleList) {
			if (module.getType().toString() == "AIRLOCK") {
				baseContainer[centerY + airlockY][centerX + airlockX] = module;
			}
		}

		// find food and water storage
		for (Module module : moduleList) {
			if (module.getType().toString() == "FOOD_WATER_STORAGE") {
				baseContainer[centerY + foodY][centerX + foodX] = module;
			}
		}

		// find control
		for (Module module : moduleList) {
			if (module.getType().toString() == "CONTROL") {
				baseContainer[centerY + controlY][centerX + controlX] = module;
			}
		}

		// find power
		for (Module module : moduleList) {
			if (module.getType().toString() == "POWER") {
				baseContainer[centerY + powerY][centerX + powerX] = module;
			}
		}

		// find sanitation
		for (Module module : moduleList) {
			if (module.getType().toString() == "SANITATION") {
				baseContainer[centerY + sanY][centerX + sanX] = module;
			}
		}

		// find dorms
		for (Module module : moduleList) {
			if (module.getType().toString() == "DORMITORY") {
				baseContainer[centerY + dormY][centerX + dormX] = module;
			}
		}

	}

	public void display(){
		map.clear();
		mapPanel.markUnusable();
		// set the new map
				int inverter = 0;
				for (int row = rows - 1; row >= 0; row--) {
					for (int col = columns - 1; col >= 0; col--) {
						if (baseContainer[row][col] != null) {
							map.setWidget(inverter, col, new Image("images/"
									+ baseContainer[row][col].getType() + ".jpg"));
						}
						else if(notInBase[row][col] != null){
							map.setWidget(inverter, col, new Image("images/"
									+ notInBase[row][col].getType() + ".jpg"));
						}
					}
					inverter++;
				}
	}
	/**
	 * Calculates the total manhatten movements from the present module locations to 
	 * the previewed base location.
	 * @return The total Manhatten distance movements to build this base.
	 */
	public int calculateMovementPoints() {
		int movementPoints = 0;
		for (int row = (rows - 1); row >= 0; row--) {
			for (int col = (columns - 1); col >= 0; col--) {
				if (baseContainer[row][col] != null) {
					movementPoints = movementPoints
							+ Math.abs(row - baseContainer[row][col].getY());
					movementPoints = movementPoints
							+ Math.abs(col - baseContainer[row][col].getX());
				}
			}
		}
		return movementPoints;
	}

	/**
	 * Finds the center X value for the current Modules
	 * 
	 * @param container
	 *            The 2d container holding the modules
	 * @return An integer value representing the mean value of X
	 */
	public int findCenterX(Module[][] container) {
		int tempX = 0;
		int numModules = 0;
		for (int row = 49; row >= 0; row--) {
			for (int col = 99; col >= 0; col--) {
				if (container[row][col] != null) {
					numModules++;
					tempX = tempX + container[row][col].getX();
				}
			}
		}
		tempX = tempX / numModules;
		if (tempX == 0) {
			tempX = 1;
		}
		if (tempX == columns - 1) {
			tempX = columns - 3;
		}

		return tempX;
	}

	/**
	 * Finds the center Y value for the current Modules
	 * 
	 * @param container
	 *            The 2d container holding the modules
	 * @return An integer value representing the mean value of Y
	 */
	public int findCenterY(Module[][] container) {
		int tempY = 0;
		int numModules = 0;
		for (int row = (rows - 1); row >= 0; row--) {
			for (int col = (columns - 1); col >= 0; col--) {
				if (container[row][col] != null) {
					numModules++;
					tempY = tempY + container[row][col].getY();
				}
			}
		}
		tempY = tempY / numModules;
		if (tempY == 0) {
			tempY = 1;
		}
		if (tempY == rows - 1) {
			tempY = rows - 3;
		}
		return tempY;
	}
	
	public void commit(){
		//empty the master module list
		model.getModuleList().clear();
		
		//change the x,y values of all modules in the base container to reflect
		// their previewed location and repopulate the master list
		for(int i = 0; i < rows; i++){
			for(int j = 0; j < columns; j++){
				if(baseContainer[i][j] != null){
					baseContainer[i][j].setX(j);
					baseContainer[i][j].setY(i);
					model.getModuleList().add(baseContainer[i][j]);
				}
			}
		}
		
	}
	/**
	 * This method sets the manhattanDistanceToBase integer variable for the Module 
	 * taken in as a parameter with respect to its placement in this base.
	 * @param m The Module whose manhattanDistanceToBase variable will be set.
	 */
	public void setManhattenDistanceToBase(Module m){
		int movementPoints = 0;
		for (int row = (rows - 1); row >= 0; row--) {
			for (int col = (columns - 1); col >= 0; col--) {
				if (baseContainer[row][col] != null) {
					if(baseContainer[row][col].equals(m)){
						movementPoints = movementPoints
								+ Math.abs(row - baseContainer[row][col].getY());
						movementPoints = movementPoints
								+ Math.abs(col - baseContainer[row][col].getX());
					}
				}
			}
		}
		m.setManhattanDistanceToBase(movementPoints);
	}
	
	/**
	 * This method is meant to display the route for the given Module from its current
	 * location to its location in the base.
	 * @param index The index in the routing list of the Module to be routed.
	 */
	public void route(int index){
		display();
		if(index < 0){
			index = 0;
		}
		if(index >= routingList.size()){
			index = routingList.size() - 1;
		}
		
		//get the module to show the route for. 
		Module routeModule = routingList.get(index);
		//iterate through the base container to find the Module to route.
		for (int row = (rows - 1); row >= 0; row--) {
			for (int col = (columns - 1); col >= 0; col--) {
				if (baseContainer[row][col] != null) {
					if(baseContainer[row][col].equals(routeModule)){
						
						
						//set the map to represent the original location of the Module being routed.
						map.setWidget(yInverter(routeModule.getY()), routeModule.getX(), 
								new Image("images/" + routeModule.getType() + ".jpg"));
						
						//get the original x and y values of the Module being routed.
						int y = routeModule.getY();
						int x = routeModule.getX();
						
						
						while(y > row){
							map.setWidget(yInverter(y - 1), x, new Image("images/down.jpg"));
							y--;
						}
						while(x > col){
							map.setWidget(yInverter(y), x - 1, new Image("images/left.jpg"));
							x--;
						}
						while(x < col){
							map.setWidget(yInverter(y), x + 1, new Image("images/right.jpg"));
							x++;
						}
						while(y < row){
							map.setWidget(yInverter(y + 1), x, new Image("images/up.jpg"));
							y++;
						}
					}
				}
			}
		}
	}
	
	
	public void setRoutingListOrder(){
		ArrayList<Module> plainList = new ArrayList<Module>();
		ArrayList<Module> otherList = new ArrayList<Module>();
		for(Module module : model.getModuleList()){
			setManhattenDistanceToBase(module);
			if(module.getType().toString() == "PLAIN"){
				plainList.add(module);
			}
			else{
				otherList.add(module);
			}
		}
		Collections.sort(plainList);
		Collections.sort(otherList);
		for(Module module : plainList){
			routingList.add(module);
		}
		for(Module module : otherList){
			routingList.add(module);
		}
		
	}
	
	public int getMovementPoints(){
		return movementPoints;
	}
	
	public int yInverter(int oldY){
		int invertedY = 0;
		for (int row = (rows - 1); row > oldY; row--) {
			invertedY++;
		}
		return invertedY;
	}
	
	public int getRoutingListSize(){
		return routingList.size();
	}
	
	public Module getRouteModule(int index){
		return routingList.get(index);
	}
	
	public void setCenterX(int nextX){
		centerX = nextX;
		externallySet = true;
	}
	public void setCenterY(int nextY){
		centerY = nextY;
		externallySet = true;
	}
	
	public int getCenterX(){
		return centerX;
	}
	
	public int getCenterY(){
		return centerY;
	}
	private int rows;
	private int columns;
	private Model model;
	private Grid map;
	private MapPanel mapPanel;
	private int plain1X, plain1Y, plain2X, plain2Y, plain3X, plain3Y, airlockX,
			airlockY;
	private int controlX, controlY, powerX, powerY, foodX, foodY, dormX, dormY;
	private int canteenX, canteenY, sanX, sanY;
	private Module baseContainer[][];
	private int movementPoints;
	private ArrayList<Module> routingList;
	private Module notInBase[][];
	private boolean externallySet;
	private int centerX;
	private int centerY;
}
